package com.rc.saas.tenant.transfers.alipay;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayFundAccountQueryModel;
import com.alipay.api.domain.AlipayFundTransCommonQueryModel;
import com.alipay.api.domain.AlipayFundTransPayModel;
import com.alipay.api.domain.Participant;
import com.alipay.api.request.AlipayFundAccountQueryRequest;
import com.alipay.api.request.AlipayFundTransCommonQueryRequest;
import com.alipay.api.request.AlipayFundTransUniTransferRequest;
import com.alipay.api.response.AlipayFundAccountQueryResponse;
import com.alipay.api.response.AlipayFundTransCommonQueryResponse;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import com.rc.saas.tenant.common.component.UidGenerator;
import com.rc.saas.tenant.common.constant.CommonConstant;
import com.rc.saas.tenant.common.vo.TResponse;
import com.rc.saas.tenant.model.acct.AcctWithdrawOrder;
import com.rc.saas.tenant.model.acct.TenantOperatorAcctSetting;
import com.rc.saas.tenant.model.operator.TenantOperator;
import com.rc.saas.tenant.model.tenant.Tenant;
import com.rc.saas.tenant.model.tenant.TenantSetting;
import com.rc.saas.tenant.service.acct.AcctWithdrawOrderService;
import com.rc.saas.tenant.service.tenant.TenantService;
import com.rc.saas.tenant.service.tenant.TenantSettingService;
import com.rc.saas.tenant.transfers.alipay.config.AliPayConfig;
import com.rc.saas.tenant.transfers.alipay.vo.AlipayTransPayWalletVo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Date;

/**
 * 单笔转账至支付宝接口,两个支付宝账户之间转账，7*24小时实时到账
 * “转账到支付宝账户”的准入条件是什么？仅支持注册大于 90 天，且已实名认证的企业支付宝账户签约，暂时不支持个体工商户及个人支付宝账户
 * Created by Sven on 2021/01/28
 */
@Service
public class AliPayWalletServiceImpl implements AliPayWalletService {
    private static Logger logger = LogManager.getLogger(AliPayWalletServiceImpl.class);

    @Autowired
    private AliPayConfig aliPayConfig;

    @Autowired
    private UidGenerator uidGenerator;

    @Autowired
    private TenantSettingService tenantSettingService;

    @Autowired
    private TenantService tenantService;

    @Autowired
    private AcctWithdrawOrderService acctWithdrawOrderService;

    @Override
    public TResponse transPayWallet(HttpServletRequest request, TenantOperator tenantOperator, AlipayTransPayWalletVo alipayTransPayWalletVo, TenantOperatorAcctSetting acctSetting) {
        if (alipayTransPayWalletVo.getTransAmount().compareTo(BigDecimal.ZERO) <= 0) {
            logger.error("转账金额不合法:{}", alipayTransPayWalletVo.getTransAmount());
            return TResponse.FAIL("转账金额不合法:" + alipayTransPayWalletVo.getTransAmount());
        }

        //根据不同的运营商简码生成充值订单号
        Tenant tenant = tenantService.selectByPrimaryKey(tenantOperator.getTenantCode());
        String outTradeNo = uidGenerator.genAlipayTransferOutBizNo(tenant.getTenantPrefix(), tenantOperator.getTenantOperatorCode(), CommonConstant.ALIPAY_TRANSFER_PREFIX);
        if (outTradeNo == null) {
            return TResponse.FAIL("无法生成转账单号");
        }

        TenantSetting tenantSetting = tenantSettingService.selectByPrimaryKey(tenantOperator.getTenantCode());

        //1 查询自己余额是否足够
        AlipayFundAccountQueryResponse queryResponse = accountQuery(assembleClientParams(tenantSetting), alipayTransPayWalletVo, tenantSetting);
        if (queryResponse == null || !queryResponse.isSuccess()) {
            logger.error("账户余额查询接口异常:{}", queryResponse.getBody());
            return TResponse.FAIL("账户余额查询接口异常");
        }

        //账户可用余额，单位元，精确到小数点后两位。示例值：26.45
        logger.warn("appId:" + aliPayConfig.getAppId() + ":账户可用余额:{}元", queryResponse.getAvailableAmount());
        if (alipayTransPayWalletVo.getTransAmount().compareTo(new BigDecimal(queryResponse.getAvailableAmount())) > 0) {
            logger.error("账户余额不足,可用余额:{},本次申请转帐金额:{}", queryResponse.getAvailableAmount(), alipayTransPayWalletVo.getTransAmount());
            return TResponse.FAIL("账户余额不足");
        }

        //生成转帐单
        AcctWithdrawOrder acctWithdrawOrder = new AcctWithdrawOrder();
        acctWithdrawOrder.setTenantCode(tenantOperator.getTenantCode());
        acctWithdrawOrder.setTenantOperatorCode(tenantOperator.getTenantOperatorCode());

        //收款账户类型,0 对公（在金融机构开设的公司账户）,如果银行卡为对公，必须传递省市支行信息或者联行号 1对私（在金融机构开设的个人账户）
        acctWithdrawOrder.setAccountType(alipayTransPayWalletVo.getAccountType());
        //支付宝的会员ID
        acctWithdrawOrder.setAlipayUserId(alipayTransPayWalletVo.getAlipayUserId());

        //提现类型 0平台转帐 1网银转帐 默认0
        acctWithdrawOrder.setTransferType(alipayTransPayWalletVo.getWithdrawType());
        //申请来源 0运营商后台  默认0
        acctWithdrawOrder.setTransferSource(alipayTransPayWalletVo.getApplySource());
        acctWithdrawOrder.setTransferBy(alipayTransPayWalletVo.getWithdrawBy());

        acctWithdrawOrder.setOutTradeNo(outTradeNo);
        acctWithdrawOrder.setWithdrawApplyId(alipayTransPayWalletVo.getWithdrawApplyId());//申请单ID
        acctWithdrawOrder.setAmount(alipayTransPayWalletVo.getTransAmount());//单位（元）

        //到帐状态 0未到帐  1已到帐 2转账失败 默认0
        acctWithdrawOrder.setStatus("0");
        acctWithdrawOrder.setCreatedTime(new Date());
        acctWithdrawOrderService.insertSelectiveReturnId(acctWithdrawOrder);

        logger.info(tenantOperator.getTenantCode() + "-" + tenantOperator.getTenantOperatorCode() + ":准备发起单笔转账至支付宝接口:{}", alipayTransPayWalletVo.toString());

        try {
            // 2 创建AlipayClient实例
            AlipayClient alipayClient = new DefaultAlipayClient(assembleClientParams(tenantSetting));

            // 3 创建使用的Open API对应的Request请求对象
            AlipayFundTransUniTransferRequest uniTransferRequest = uniTransfer(outTradeNo, tenantOperator, alipayTransPayWalletVo, acctSetting);

            // 4 发起请求并处理响应
            AlipayFundTransUniTransferResponse transferResponse = alipayClient.certificateExecute(uniTransferRequest);

            //如果转账接口同步返回的 status 属性为 SUCCESS 则表示转账成功；同步调用若发生超时等异常情况，可以通过异步通知或者调用转账查询接口做主动查询
            if (!transferResponse.isSuccess()) {
                logger.error("支付宝转账失败:{},:{},:{}", transferResponse.getMsg(), transferResponse.getSubMsg(), transferResponse.getBody());
                return TResponse.FAIL("支付宝转账失败,"+transferResponse.getSubMsg());
            }

            //6 查转账单据状态
            AlipayFundTransCommonQueryResponse commonQueryResponse = queryTransferInfo(assembleClientParams(tenantSetting), transferResponse);
            if (commonQueryResponse == null || !commonQueryResponse.isSuccess()) {
                logger.error("查帐接口异常:{},:{},:{}", commonQueryResponse.getMsg(), commonQueryResponse.getSubMsg(), commonQueryResponse.getBody());
                return TResponse.FAIL("查帐接口异常");
            }

            //转账单据状态
            if (commonQueryResponse.getStatus().equals("SUCCESS")) {
                logger.info(tenantOperator.getTenantCode() + "-" + tenantOperator.getTenantOperatorCode() + ":支付宝转账成功，准备入账:{}", alipayTransPayWalletVo.getTransAmount());

                //7 入帐
                acctWithdrawOrderService.updateSuccessAcctWithdrawOrder(transferResponse, acctWithdrawOrder.getWithdrawOrderId());
            }

            logger.info(tenantOperator.getTenantCode() + "-" + tenantOperator.getTenantOperatorCode() + ":支付宝转帐成功:{}", alipayTransPayWalletVo.getTransAmount());

            TResponse tResponse = TResponse.SUCCESS("转帐成功");
            tResponse.put(acctWithdrawOrder);
            return tResponse;
        } catch (Exception e) {
            logger.error("支付宝转账失败异常", e);
            return TResponse.FAIL("支付宝转账失败");
        }
    }

    @Override
    public AlipayFundAccountQueryResponse accountQuery(CertAlipayRequest certAlipayRequest, AlipayTransPayWalletVo alipayTransPayWalletVo, TenantSetting tenantSetting) {
        try {
            //test
            certAlipayRequest.setAppId(tenantSetting.getAlipayAppid());

            DefaultAlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
            AlipayFundAccountQueryRequest request = new AlipayFundAccountQueryRequest();

            //request.setBizContent("{" +
            //        "\"alipay_user_id\":\"208818457777488333\"," +
            //        "\"account_type\":\"ACCTRANS_ACCOUNT\"" +
            //        "  }");

            AlipayFundAccountQueryModel model = new AlipayFundAccountQueryModel();
            //在沙箱环境下商户UID即为PID
            //在沙箱环境下是商户UID2088102168917625
            //model.setAlipayUserId("2088102168917625");
            //支付方的支付宝帐号,必须等于请求发起方PID
            //model.setAlipayUserId("2088102168917625");
            model.setAlipayUserId(tenantSetting.getAlipayUserId());

            //查询的账号类型，固定值为 ACCTRANS_ACCOUNT。
            model.setAccountType("ACCTRANS_ACCOUNT");
            request.setBizModel(model);

            return alipayClient.certificateExecute(request);
        } catch (Exception e) {
            logger.error("账户余额查询接口alipay.fund.account.query 异常", e);
        }

        return null;
    }

    @Override
    public AlipayFundTransCommonQueryResponse queryTransferInfo(CertAlipayRequest certAlipayRequest, AlipayFundTransUniTransferResponse transferResponse) {
        try {
            DefaultAlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);
            AlipayFundTransCommonQueryRequest request = new AlipayFundTransCommonQueryRequest();

            //request.setBizContent("{" +
            //        "\"product_code\":\"TRANS_ACCOUNT_NO_PWD\"," +
            //        "\"biz_scene\":\"DIRECT_TRANSFER\"," +
            //        "\"out_biz_no\":\"20190619000000001\"," +
            //        "\"order_id\":\"20190619110070000006800000025890\"" +
            //        "  }");

            AlipayFundTransCommonQueryModel model = new AlipayFundTransCommonQueryModel();

            model.setOrderId(transferResponse.getOrderId());
            model.setPayFundOrderId(transferResponse.getPayFundOrderId());
            model.setOutBizNo(transferResponse.getOutBizNo());

            //描述特定的业务场景，如果传递了out_biz_no 则该字段为必传。单笔无密转账固定为 DIRECT_TRANSFER。
            model.setBizScene("DIRECT_TRANSFER");

            //销售产品码，如果传了 out_biz_no，则该字段必传。单笔无密转账固定为 TRANS_ACCOUNT_NO_PWD。
            model.setProductCode("TRANS_ACCOUNT_NO_PWD");

            request.setBizModel(model);
            return alipayClient.certificateExecute(request);
        } catch (Exception e) {
            logger.error("单据查询接口 alipay.fund.trans.common.query 异常", e);
        }

        return null;
    }

    /**
     * 支付宝配置
     *
     * @return
     */
    private CertAlipayRequest assembleClientParams(TenantSetting tenantSetting) {
        CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
        //设置网关地址
        certAlipayRequest.setServerUrl(aliPayConfig.getServerUrl());
        //设置应用Id
        certAlipayRequest.setAppId(aliPayConfig.getAppId());
        //certAlipayRequest.setAppId("2016072800113392");

        //设置PKCS8格式的应用私钥
        //certAlipayRequest.setPrivateKey(aliPayConfig.getPrivateKey());
        certAlipayRequest.setPrivateKey(tenantSetting.getAlipayPrivateKey());

        //设置请求格式，固定值json
        certAlipayRequest.setFormat(aliPayConfig.getFormat());
        //设置字符集
        certAlipayRequest.setCharset(aliPayConfig.getCharset());
        //设置签名类型
        certAlipayRequest.setSignType(aliPayConfig.getSignType());

        //如果是生产环境或者预演环境，则使用代理模式
        //if ("prod".equals(activeEnv) || "stage".equals(activeEnv) || "test".equals(activeEnv)) {
        //设置应用公钥证书路径
        //certAlipayRequest.setCertPath(aliPayConfig.getCertPath());
        certAlipayRequest.setCertPath(tenantSetting.getAlipayAcctCertpath());

        //设置支付宝公钥证书路径
        //certAlipayRequest.setAlipayPublicCertPath(aliPayConfig.getPublicCertPath());
        certAlipayRequest.setAlipayPublicCertPath(tenantSetting.getAlipayAcctPublicCertpath());

        //设置支付宝根证书路径
        //certAlipayRequest.setRootCertPath(aliPayConfig.getRootCertPath());
        certAlipayRequest.setRootCertPath(tenantSetting.getAlipayAcctRootCertpath());

        return certAlipayRequest;
    }

    /**
     * 转账请求入参
     * https://opendocs.alipay.com/open/309/106236
     *
     * @return
     */
    private AlipayFundTransUniTransferRequest uniTransfer(String outTradeNo, TenantOperator tenantOperator, AlipayTransPayWalletVo alipayTransPayWalletVo, TenantOperatorAcctSetting acctSetting) {
        // 初始化Request，并填充Model属性。实际调用时请替换为您想要使用的API对应的Request对象。
        AlipayFundTransUniTransferRequest bizQueryRequest = new AlipayFundTransUniTransferRequest();

        //转账参数
        AlipayFundTransPayModel model = new AlipayFundTransPayModel();
        model.setOutBizNo(outTradeNo);//商户端的唯一订单号，对于同一笔转账请求，商户需保证该订单号唯一
        model.setTransAmount(String.valueOf(alipayTransPayWalletVo.getTransAmount()));//订单总金额，单位为元，精确到小数点后两位，取值范围[0.1,100000000]。

        model.setProductCode("TRANS_ACCOUNT_NO_PWD");//销售产品码。单笔无密转账固定为 TRANS_ACCOUNT_NO_PWD。
        model.setBizScene("DIRECT_TRANSFER");//业务场景。单笔无密转账固定为 DIRECT_TRANSFER。

        String title = "结算款";

        //收款方信息
        Participant participant = new Participant();

        //参与方的标识类型，目前支持如下枚举：
        //ALIPAY_USER_ID：支付宝的会员 ID。
        //ALIPAY_LOGON_ID：支付宝登录号，支持邮箱和手机号格式。
        //TODO 生产环境使用
//        participant.setIdentityType("ALIPAY_USER_ID");
        //买家USER_ID
        participant.setIdentity(alipayTransPayWalletVo.getAlipayUserId());

        //沙箱环境下找不到买家USER_ID，只能使用ALIPAY_LOGON_ID类型
//        participant.setIdentityType("ALIPAY_LOGON_ID");
//        participant.setIdentity("hpccbd3956@sandbox.com");

        participant.setIdentityType("ALIPAY_LOGON_ID");
//        participant.setIdentity("455322144@qq.com");

        //参与方真实姓名。如果非空，将校验收款支付宝账号姓名一致性。当 identity_type=ALIPAY_LOGON_ID 时，本字段必填
//        participant.setName("张文博");
        participant.setName(alipayTransPayWalletVo.getAlipayUserName());
        model.setPayeeInfo(participant);

        model.setOrderTitle(title);//转账业务的标题，用于在支付宝用户的账单里显示
        model.setRemark(title);//业务备注

        bizQueryRequest.setBizModel(model);
        return bizQueryRequest;
    }


}
